#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <getopt.h>
#include "base64_utils.h"

#define MAX_SIZE 4095
#define COMMAND_SIZE 512

char buf[MAX_SIZE+1];

// receiver: mail address of the recipient
// subject: mail subject
// msg: content of mail body or path to the file containing mail body
// att_path: path to the attachment
void send_mail(const char* receiver, const char* subject, const char* msg, const char* att_path)
{
    const char* end_msg = "\r\n.\r\n";
    const char* line_break = "\r\n";
    const char* host_name = "smtp.qq.com"; // TODO: Specify the mail server domain name
    const unsigned short port = 25; // SMTP server port
    const char* user = ""; // TODO: Specify the user
    const char* pass = ""; // TODO: Specify the password
    const char* from = ""; // TODO: Specify the mail address of the sender
    char dest_ip[16]; // Mail server IP address
    int s_fd; // socket file descriptor
    struct hostent *host;
    struct in_addr **addr_list;
    int i = 0;
    int r_size;

    // Get IP from domain name
    if ((host = gethostbyname(host_name)) == NULL)
    {
        herror("gethostbyname");
        exit(EXIT_FAILURE);
    }

    addr_list = (struct in_addr **) host->h_addr_list;
    while (addr_list[i] != NULL)
        ++i;
    strcpy(dest_ip, inet_ntoa(*addr_list[i-1]));

    // TODO: Create a socket, return the file descriptor to s_fd, and establish a TCP connection to the mail server
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = ((port >> 8) & 0xff) | ((port << 8 & 0xff00));
    bzero(server_addr.sin_zero, 8);
    server_addr.sin_addr.s_addr = inet_addr(dest_ip);
    
    if((s_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
        perror("socket error");
        exit(EXIT_FAILURE);
    }

    if(connect(s_fd, &server_addr, sizeof(struct sockaddr_in)) == -1){
        perror("connect error");
        exit(EXIT_FAILURE);
    }

    // Print welcome message
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    // TODO: All the send functions need error handler, but not for now

    // Send EHLO command and print server response
    const char* EHLO = "EHLO qq.com\r\n"; // TODO: Enter EHLO command here
    send(s_fd, EHLO, strlen(EHLO), 0);


    // TODO: Print server response to EHLO command
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);
    
    // TODO: Authentication. Server response should be printed out.
    const char* AUTH = "AUTH login\r\n";
    send(s_fd, AUTH, strlen(AUTH), 0);
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    char* USER = encode_str(user);
    char* PASS = encode_str(pass);

    // send username
    send(s_fd, USER, strlen(USER), 0);
    send(s_fd, line_break, strlen(line_break), 0);
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    // send password
    send(s_fd, PASS, strlen(PASS), 0);
    send(s_fd, line_break, strlen(line_break), 0);
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    free(USER);
    free(PASS);

    // TODO: Send MAIL FROM command and print server response
    char mail_from[COMMAND_SIZE];
    sprintf(mail_from, "%s%s%s", "MAIL FROM:<", from, ">\r\n");
    send(s_fd, mail_from, strlen(mail_from), 0);
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    // TODO: Send RCPT TO command and print server response
    char rcpt_to[COMMAND_SIZE];
    sprintf(rcpt_to, "%s%s%s", "RCPT TO:<", receiver, ">\r\n");
    send(s_fd, rcpt_to, strlen(rcpt_to), 0);
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    // TODO: Send DATA command and print server response
    const char* DATA = "data\r\n";
    send(s_fd, DATA, strlen(DATA), 0);
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    // TODO: Send message data
    const char* MIME_VERSION = "Mime-Version: 1.0\r\n";
    const char* CONTENT_TYPE = "Content-Type: multipart/mixed; boundary=h1tszmaillab\r\n";
    const char* TEXT_TYPE = "Content-Type: text/plain\r\n\r\n";
    const char* ATTA_TYPE = "Content-Type: application/octet-stream\r\n"; 
    const char* ENCODING = "Content-Transfer-Encoding: base64\r\n";
    const char* BOUNDARY = "--h1tszmaillab\r\n";
    const char* BOUNDARY_END = "--h1tszmaillab--\r\n";
    const char* DISPOSITION = "Content-Disposition: attachment; name=\"attachment.txt\"\r\n";
    char subject_data[COMMAND_SIZE];
    char from_data[COMMAND_SIZE];
    sprintf(subject_data, "%s%s%s", "subject:", subject, line_break);
    sprintf(from_data, "%s%s%s", "from:", from, line_break);

    // parser the attachment (without encode)
    char atta_data[MAX_SIZE+1];
    FILE *atta_file;
    if((atta_file = fopen(att_path, "r")) == NULL){
        perror("open attachment");
        exit(EXIT_FAILURE);
    }
    fscanf(atta_file,"%s",atta_data);
    fclose(atta_file);
    printf("att: %s\n", atta_data);
    
    // send header
    send(s_fd, subject_data, strlen(subject_data), 0);
    send(s_fd, from_data, strlen(from_data), 0);
    send(s_fd, MIME_VERSION, strlen(MIME_VERSION), 0);
    send(s_fd, CONTENT_TYPE, strlen(CONTENT_TYPE), 0);

    // send attachment
    send(s_fd, BOUNDARY, strlen(BOUNDARY), 0);
    send(s_fd, ATTA_TYPE, strlen(ATTA_TYPE), 0);
    send(s_fd, DISPOSITION, strlen(DISPOSITION), 0);
    send(s_fd, atta_data, strlen(atta_data), 0);
    send(s_fd, line_break, strlen(line_break), 0);


    // send message
    send(s_fd, BOUNDARY, strlen(BOUNDARY), 0);
    send(s_fd, TEXT_TYPE, strlen(TEXT_TYPE), 0);
    send(s_fd, msg, strlen(msg), 0);
    send(s_fd, line_break, strlen(line_break), 0);

    // TODO: Message ends with a single period
    send(s_fd, end_msg, strlen(end_msg), 0);
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    // TODO: Send QUIT command and print server response
    const char* QUIT = "quit\r\n";
    send(s_fd, QUIT, strlen(QUIT), 0);
    if ((r_size = recv(s_fd, buf, MAX_SIZE, 0)) == -1)
    {
        perror("recv");
        exit(EXIT_FAILURE);
    }
    buf[r_size] = '\0'; // Do not forget the null terminator
    printf("%s", buf);

    close(s_fd);
}




int main(int argc, char* argv[])
{
    int opt;
    char* s_arg = NULL;
    char* m_arg = NULL;
    char* a_arg = NULL;
    char* recipient = NULL;
    const char* optstring = ":s:m:a:";
    while ((opt = getopt(argc, argv, optstring)) != -1)
    {
        switch (opt)
        {
        case 's':
            s_arg = optarg;
            break;
        case 'm':
            m_arg = optarg;
            break;
        case 'a':
            a_arg = optarg;
            break;
        case ':':
            fprintf(stderr, "Option %c needs an argument.\n", optopt);
            exit(EXIT_FAILURE);
        case '?':
            fprintf(stderr, "Unknown option: %c.\n", optopt);
            exit(EXIT_FAILURE);
        default:
            fprintf(stderr, "Unknown error.\n");
            exit(EXIT_FAILURE);
        }
    }

    if (optind == argc)
    {
        fprintf(stderr, "Recipient not specified.\n");
        exit(EXIT_FAILURE);
    }
    else if (optind < argc - 1)
    {
        fprintf(stderr, "Too many arguments.\n");
        exit(EXIT_FAILURE);
    }
    else
    {
        recipient = argv[optind];
        send_mail(recipient, s_arg, m_arg, a_arg);
        exit(0);
    }
}
